#Required libraries
# Tidyverse for data science and exploration
require(dplyr)
require(tidyr)
require(readr)
require(tibble)
require(stringr)
require(purrr)
require(forcats)
require(rlang)
# enhances tidyverse
require(tidylog) # additional logging
require(magrittr) # additional data pipe syntax
# for reading data in multiple formats
require(readxl)
require(haven)
# visual analysis
require(ggplot2)
require(GGally) # extensions to ggplot
require(gt) # well formatted tables
# client-side interactive publishable graphics
require(plotly)
require(leaflet)
require(crosstalk)
require(htmlwidgets)
# server-side interactive graphics
require(shiny)
require(shinyjs)
# Canned Interactive EDA
require(ExPanDaR)
Exploring KU Book Processing Charges
# read KU data frame
KUbpc.df <- read_csv("Public Data/openapc-de/data/bpc.csv")
Parsed with column specification:
cols(
institution = col_character(),
period = col_double(),
euro = col_double(),
doi = col_character(),
backlist_oa = col_logical(),
publisher = col_character(),
book_title = col_character(),
isbn = col_character(),
isbn_print = col_character(),
isbn_electronic = col_character(),
license_ref = col_character(),
indexed_in_crossref = col_logical(),
doab = col_logical()
)
# read DOAB metadata
source('Public Data/DOAB/doabingest.R')
DOABmeta.df <- doabFetch()
embedded nul(s) found in input
head(KUbpc.df)
head(summary(KUbpc.df))
institution period euro doi
Length:938 Min. :2017 Min. :1075 Length:938
Class :character 1st Qu.:2017 1st Qu.:1875 Class :character
Mode :character Median :2018 Median :1981 Mode :character
Mean :2018 Mean :4368
3rd Qu.:2019 3rd Qu.:8250
Max. :2020 Max. :8978
backlist_oa publisher book_title isbn
Mode :logical Length:938 Length:938 Length:938
FALSE:357 Class :character Class :character Class :character
TRUE :581 Mode :character Mode :character Mode :character
isbn_print isbn_electronic license_ref
Length:938 Length:938 Length:938
Class :character Class :character Class :character
Mode :character Mode :character Mode :character
indexed_in_crossref doab
Mode :logical Mode :logical
FALSE:127 FALSE:44
TRUE :811 TRUE :894
ggplot(data = KUbpc.df, aes(KUbpc.df$institution)) + geom_bar()

ggplot(data = KUbpc.df, aes(KUbpc.df$euro)) + geom_histogram()

General Exploratory Data Analysis
ggplot(data = KUbpc.df) + geom_bar(mapping = aes(x = KUbpc.df$doab))

# Date to Doab
date_doab <- KUbpc.df %>% ggplot(data = KUbpc.df, mapping = aes(x = KUbpc.df$period, colour = KUbpc.df$doab)) + geom_freqpoly(binwidth = 0.1)
ggplotly(date_doab)
# publisher_euro <- KUbpc.df %>%
# ggplot(data = KUbpc.df, mapping = aes(x = KUbpc.df$publisher, colour = KUbpc.df$euro)) + geom_freqpoly(binwidth = 0.1)
# Institution to Euro
institution_euro <- KUbpc.df %>% ggplot(data = KUbpc.df, mapping = aes(x = KUbpc.df$euro)) + geom_freqpoly(mapping = aes(colour = KUbpc.df$institution), binwidth = 500)
ggplotly(institution_euro)
NA
Idea: Publishers vs. Charges
Question: How do the top 25% of publishers divide up charges (in Euro)?
Observation: Charges are grouped around ~2000 Euros and ~8000 Euros.
publisher_counts <- KUbpc.df %>%
group_by(publisher) %>%
tally
tally: now 110 rows and 2 columns, ungrouped
sorted_counts = arrange(publisher_counts, desc(n))
total_n = sum(sorted_counts$n)
quarter_n = 0.25 * total_n
new_n = sum(sorted_counts$n[0:6])
sorted_counts %>% filter(n > 24)
# filtered <- filter(KUbpc.df$publisher %in% sorted_counts$publisher)
filtered <- filter(KUbpc.df, KUbpc.df$publisher == 'transcript Verlag' |
KUbpc.df$publisher == 'Duke University Press' |
KUbpc.df$publisher == 'University of Michigan Press' |
KUbpc.df$publisher == 'Manchester University Press' |
KUbpc.df$publisher == 'Pluto Press' |
KUbpc.df$publisher == 'Liverpool University Press')
head(filtered)
euro_publisher <- filtered %>%
ggplot(data = filtered, mapping = aes(x = filtered$publisher, y = filtered$euro),
aes(x = filtered$publisher, y = filtered$euro)) +
# geom_count(aes(color = ..n.., size = after_stat(prop), group = euro)) +
geom_count(aes(color = ..n.., group = euro)) +
scale_size_area(max_size = 10) +
theme(axis.text = element_text(size = rel(0.75))) +
labs(title = "How Publishers Divide Charges", x = "Top 25% of Publishers", y = "Price (Euro)", color = 'Number of Copies') +
scale_x_discrete(labels = function(x) str_wrap(str_replace_all(x, "foo", " "), width = 17))
# ggplot:
ggplotly(euro_publisher)
# crosstalk:
ft <- highlight_key(filtered)
gg_ft <- ggplot(data = ft, mapping = aes(x = filtered$publisher, y = filtered$euro)) +
geom_count(aes(color = ..n.., size = after_stat(prop), group = euro)) +
labs(title = "How Publishers Divide Charges", x = "Top 25% of Publishers", y = "Price (Euro)", color = 'Number of Copies') +
scale_x_discrete(labels = function(x) str_wrap(str_replace_all(x, "foo", " "), width = 17))
cross_ft <- bscols(
filter_select("publisher", "Select a publisher", ft, ~publisher),
ggplotly(gg_ft, dynamicTicks = TRUE),
widths = c(12, 12)
)
All elements of `...` must be named.
Did you want `key = c(key)`?Sum of bscol width units is greater than 12
bscols(cross_ft)
# shared_euro_publisher <- SharedData$new(filtered)
# leaflet(shared_euro_publisher) %>% addMarkers()
# data.table::data.table(shared_euro_publisher)
Idea: Publishers’ Charges vs. Year/OA Type
Sub-Question: What best explains the particular division of charges? (Year, OA Type)
Observation: The low and high charge groups seem to be defined by the type of OA business model, whereas the slight differences within each group seem to be defined by the year.
head(filtered)
# Does Type of OA impact the particular division of charges?
euro_oa_publisher <- filtered %>%
ggplot(data = filtered, mapping = aes(x = filtered$backlist_oa, y = filtered$euro),
aes(x = filtered$backlist_oa, y = filtered$euro)) +
geom_count(aes(color = ..n.., group = euro)) +
scale_size_area(max_size = 10) +
theme(axis.text = element_text(size = rel(0.75))) +
labs(title = "How OA Impacts Price Division of Charges", x = "Type of OA", y = "Price (Euro)", color = 'Number of Copies')
# ggplot:
ggplotly(euro_oa_publisher)
# crosstalk:
ft <- highlight_key(filtered)
gg_ft <- ggplot(data = ft, mapping = aes(x = filtered$backlist_oa, y = filtered$euro)) +
geom_count(aes(color = ..n.., size = after_stat(prop), group = euro)) +
labs(title = "How OA Impacts Division of Charges", x = "Type of OA", y = "Price (Euro)", color = 'Number of Copies')
cross_ft <- bscols(
filter_select("publisher", "Select a publisher", ft, ~publisher),
ggplotly(gg_ft, dynamicTicks = TRUE),
widths = c(12, 12)
)
All elements of `...` must be named.
Did you want `key = c(key)`?Sum of bscol width units is greater than 12
bscols(cross_ft)
# Does Year impact the particular division of charges?
euro_year_publisher <- filtered %>%
ggplot(data = filtered, mapping = aes(x = filtered$period, y = filtered$euro),
aes(x = filtered$period, y = filtered$euro)) +
geom_count(aes(color = ..n.., group = euro)) +
scale_size_area(max_size = 10) +
theme(axis.text = element_text(size = rel(0.75))) +
labs(title = "How Year Impacts Price Division of Charges", x = "Year", y = "Price (Euro)", color = 'Number of Copies')
# ggplot:
ggplotly(euro_year_publisher)
# crosstalk:
ft <- highlight_key(filtered)
gg_ft <- ggplot(data = ft, mapping = aes(x = filtered$period, y = filtered$euro)) +
geom_count(aes(color = ..n.., size = after_stat(prop), group = euro)) +
labs(title = "How Year Impacts Division of Charges", x = "Year", y = "Price (Euro)", color = 'Number of Copies')
cross_ft <- bscols(
filter_select("publisher", "Select a publisher", ft, ~publisher),
ggplotly(gg_ft, dynamicTicks = TRUE),
widths = c(12, 12)
)
All elements of `...` must be named.
Did you want `key = c(key)`?Sum of bscol width units is greater than 12
bscols(cross_ft)
NA
NA
Idea: Publishers vs. OA
Question: What type of business model do the top 25% publishers use?
Observation: Most have a higher proportion of True (moved to OA from traditional publishing) than False (already published OA).
oa_type <- filtered %>%
ggplot(data = filtered, mapping = aes(x = filtered$publisher, colour = filtered$backlist_oa), fill = filtered$backlist_oa) +
geom_bar(position = "fill", width = 0.7, fill="#EAEAEA") +
labs(title = "Business Model OA for Publishers", x = "Top 25% of Publishers", y = "Proportion", color = 'Types of OA') +
theme(axis.text = element_text(size = rel(0.75))) +
scale_x_discrete(labels = function(x) str_wrap(str_replace_all(x, "foo", " "), width = 17)) +
scale_color_brewer(palette = "Set1")
ggplotly(oa_type)
# crosstalk:
ft <- highlight_key(filtered)
oa_ft <- ggplot(data = ft, mapping = aes(x = ft$publisher, colour = ft$backlist_oa), fill = ft$backlist_oa) +
geom_bar(position = "fill", width = 0.7) +
labs(title = "Business Model OA for Publishers", x = "Top 25% of Publishers", y = "Proportion of Backlist OA", color = 'Types of OA') +
theme(axis.text = element_text(size = rel(0.75))) +
scale_x_discrete(labels = function(x) str_wrap(str_replace_all(x, "foo", " "), width = 17))
# cross_oa_ft <- bscols(
# filter_select("publisher", "Select a publisher", ft, ~publisher),
# ggplotly(oa_ft, dynamicTicks = TRUE),
# # widths = c(12, 12)
# )
# bscols(cross_oa_ft)
Idea: Publishers’ OA vs. Year
Question: Did OA business models of the top 25% publishers change per year?
Observation:
oa_time <- function(pub_name) {
pub_ft <- filter(filtered, filtered$publisher == pub_name)
pub_oa <- pub_ft %>%
ggplot(data = pub_ft, mapping = aes(x = pub_ft$period, colour = pub_ft$backlist_oa), fill = pub_ft$backlist_oa) +
geom_bar(position = "fill", width = 0.7, fill="#EAEAEA") +
labs(title = paste(pub_name, "'s OA Through the Years", sep = ""),
x = "Years", y = "Proportion of Backlist OA", color = 'Types of OA') +
theme(axis.text = element_text(size = rel(0.75))) +
scale_x_discrete(limits=c(2017, 2018, 2019)) +
scale_color_brewer(palette = "Set1")
ggplotly(pub_oa)
}
top25_list = c("transcript Verlag", "Duke University Press", "University of Michigan Press", "Manchester University Press", "Pluto Press", "Liverpool University Press")
oa_time("transcript Verlag")
Continuous limits supplied to discrete scale.
Did you mean `limits = factor(...)` or `scale_*_continuous()`?
oa_time("Duke University Press")
Continuous limits supplied to discrete scale.
Did you mean `limits = factor(...)` or `scale_*_continuous()`?
oa_time("University of Michigan Press")
Continuous limits supplied to discrete scale.
Did you mean `limits = factor(...)` or `scale_*_continuous()`?
oa_time("Manchester University Press")
Continuous limits supplied to discrete scale.
Did you mean `limits = factor(...)` or `scale_*_continuous()`?
oa_time("Pluto Press")
Continuous limits supplied to discrete scale.
Did you mean `limits = factor(...)` or `scale_*_continuous()`?
oa_time("Liverpool University Press")
Continuous limits supplied to discrete scale.
Did you mean `limits = factor(...)` or `scale_*_continuous()`?
Idea: Revenue vs. OA
Question: What total revenue are publishers receiving each year?
Observation:
# Finding total revenue for each publisher
revenue_finder <- function(pub_name) {
pub_filtered <- filter(filtered, filtered$publisher == pub_name)
rev = sum(pub_filtered$euro)
}
revenue_df <- data.frame("publisher" = top25_list)
revenue_list <- c()
for (i in top25_list) {
revenue_list<-c(revenue_list,revenue_finder(i))
}
revenue_df$revenue <- c(revenue_list)
print(revenue_df)
# ggplot:
publisher_revenue <- revenue_df %>%
ggplot(data = revenue_df, mapping = aes(x = revenue_df$publisher, y = revenue_df$revenue), fill = revenue_df$revenue) +
geom_col() +
labs(title = "Total Revenue for Publishers", x = "Top 25% of Publishers", y = "Revenue (Euro)", color = 'Types of OA') +
theme(axis.text = element_text(size = rel(0.75))) +
scale_x_discrete(labels = function(x) str_wrap(str_replace_all(x, "foo", " "), width = 17)) +
scale_fill_brewer(palette = "Set1")
ggplotly(publisher_revenue)
Use of `revenue_df$publisher` is discouraged. Use `publisher` instead.Use of `revenue_df$revenue` is discouraged. Use `revenue` instead.
Idea: Revenue vs. OA
Question: What revenue are publishers receiving per year?
Observation:
# Finding total revenue for each publisher
revlist_2017 <- c()
revlist_2018 <- c()
revlist_2019 <- c()
revlist <- c()
for (name in top25_list) {
pub_name <- filter(filtered, filtered$publisher == name)
rev_2017 = sum(pub_name[pub_name$period == 2017,]$euro)
revlist_2017 <- c(revlist_2017, rev_2017)
rev_2018 = sum(pub_name[pub_name$period == 2018,]$euro)
revlist_2018 <- c(revlist_2018, rev_2018)
rev_2019 = sum(pub_name[pub_name$period == 2019,]$euro)
revlist_2019 <- c(revlist_2019, rev_2019)
}
revenue_df <- data.frame("publisher" = top25_list)
revenue_df$'2017' <- c(revlist_2017)
revenue_df$'2018' <- c(revlist_2018)
revenue_df$'2019' <- c(revlist_2019)
print(revenue_df)
revenue_year <- c(revenue_df$'2017', revenue_df$'2018', revenue_df$'2019')
year <- c('2017', '2018', '2019')
# ggplot:
pub_year_revenue1 <- revenue_df %>%
ggplot(data = revenue_df, mapping = aes(x = '2017', y = revenue_df$'2017', fill = revenue_df$publisher)) +
geom_bar(position="dodge", stat="identity") +
labs(title = "Total Revenue for Publishers", x = "Year", y = "Revenue (Euro)", color = 'Publishers') +
theme(axis.text = element_text(size = rel(0.75)))
ggplotly(pub_year_revenue1)
Use of `revenue_df$"2017"` is discouraged. Use `2017` instead.Use of `revenue_df$publisher` is discouraged. Use `publisher` instead.
pub_year_revenue2 <- revenue_df %>%
ggplot(data = revenue_df, mapping = aes(x = '2018', y = revenue_df$'2018', fill = revenue_df$publisher)) +
geom_bar(position="dodge", stat="identity") +
labs(title = "Total Revenue for Publishers", x = "Year", y = "Revenue (Euro)", color = 'Publishers') +
theme(axis.text = element_text(size = rel(0.75)))
ggplotly(pub_year_revenue2)
Use of `revenue_df$"2018"` is discouraged. Use `2018` instead.Use of `revenue_df$publisher` is discouraged. Use `publisher` instead.
pub_year_revenue3 <- revenue_df %>%
ggplot(data = revenue_df, mapping = aes(x = '2019', y = revenue_df$'2019', fill = revenue_df$publisher)) +
geom_bar(position="dodge", stat="identity") +
labs(title = "Total Revenue for Publishers", x = "Year", y = "Revenue (Euro)", color = 'Publishers') +
theme(axis.text = element_text(size = rel(0.75)))
ggplotly(pub_year_revenue3)
Use of `revenue_df$"2019"` is discouraged. Use `2019` instead.Use of `revenue_df$publisher` is discouraged. Use `publisher` instead.
Continued, tried putting it into one graph.
revlist <- c()
revlist_2017 <- c()
revlist_2018 <- c()
revlist_2019 <- c()
for (name in top25_list) {
pub_name <- filter(filtered, filtered$publisher == name)
rev_2017 = sum(pub_name[pub_name$period == 2017,]$euro)
revlist_2017 <- c(revlist_2017, rev_2017)
rev_2018 = sum(pub_name[pub_name$period == 2018,]$euro)
revlist_2018 <- c(revlist_2018, rev_2018)
rev_2019 = sum(pub_name[pub_name$period == 2019,]$euro)
revlist_2019 <- c(revlist_2019, rev_2019)
}
revlist <- c(revlist_2017, revlist_2018, revlist_2019)
print(revlist)
[1] 127420 153491 43044 96849 51824 48131 58125 61875 54750 36000
[11] 53625 21375 51750 58125 36000 17625 40500 42375
nrev <- matrix(revlist, ncol=6, byrow=TRUE)
colnames(nrev) <- top25_list
rownames(nrev) <- c("2017", "2018", "2019")
nrev <- as.table(nrev)
nrev <- as.data.frame.matrix(nrev)
print(nrev)
#, nrev$`Duke University Press`, nrev$`University of Michigan Press`, nrev$`Pluto Press`, nrev$`Manchester University Press`, nrev$`Liverpool University Press`
pub_year_rev <- nrev %>%
ggplot(data = nrev, mapping = aes(x = c("2017", "2018", "2019"), y = c(nrev$"transcript Verlag"), fill = nrev$publisher)) +
geom_bar(position="dodge", stat="identity") +
labs(title = "Total Revenue for Publishers", x = "Year", y = "Revenue (Euro)", color = 'Publishers') +
theme(axis.text = element_text(size = rel(0.75)))
ggplotly(pub_year_rev)
Use of `nrev$"transcript Verlag"` is discouraged. Use `transcript Verlag` instead.Use of `nrev$publisher` is discouraged. Use `publisher` instead.
Idea: DOAB analysis
Question: What is the average time gap between year of publication and added on date?
Observation:
DOABmeta.df <- filter(DOABmeta.df, is.na(DOABmeta.df$Year.of.publication))
print(DOABmeta.df$Year.of.publication[1:4])
[1] NA NA NA NA
gap = mean(DOABmeta.df$Added.on.date - DOABmeta.df$Year.of.publication[1:3])
Error in DOABmeta.df$Added.on.date - DOABmeta.df$Year.of.publication[1:3] :
non-numeric argument to binary operator
Comparison of charges by year and backlist
# create faceted plot object
charges.plot <- KUbpc.df %>% ggplot(aes(euro))+geom_histogram(bins=6)+facet_grid(rows=vars(period), cols = vars(backlist_oa))
## Present as Standard plot
plot(charges.plot)

# this plot will render publicly https://htmlpreview.github.io/?https://github.com/MIT-Informatics/monograph/blob/master/00%20EDA%20Start.nb.html
Interactive charges exploration
ggplotly(charges.plot)
# https://mit-informatics.github.io/monograph/demo.html
### Interactive Dataset Exploration
KUbpc.df %>% ExPanD(df=. ,title="KU Book Processing Charges",export_nb_option = TRUE)
# ExPanD uses shiny() which works running R locally, but isn't going to work through github. Could publish through shinyapps.io (low usage only), or export a non-interactive notebook it
# see: https://drmaltman.shinyapps.io/demo/
LS0tDQp0aXRsZTogIkV4cGxvcmF0b3J5IEFuYWx5c2lzIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KYGBge3J9DQojUmVxdWlyZWQgbGlicmFyaWVzDQoNCiMgVGlkeXZlcnNlIGZvciBkYXRhIHNjaWVuY2UgYW5kIGV4cGxvcmF0aW9uDQpyZXF1aXJlKGRwbHlyKQ0KcmVxdWlyZSh0aWR5cikNCnJlcXVpcmUocmVhZHIpDQpyZXF1aXJlKHRpYmJsZSkNCnJlcXVpcmUoc3RyaW5ncikNCnJlcXVpcmUocHVycnIpDQpyZXF1aXJlKGZvcmNhdHMpDQpyZXF1aXJlKHJsYW5nKQ0KDQojIGVuaGFuY2VzIHRpZHl2ZXJzZQ0KcmVxdWlyZSh0aWR5bG9nKSAjIGFkZGl0aW9uYWwgbG9nZ2luZw0KcmVxdWlyZShtYWdyaXR0cikgIyBhZGRpdGlvbmFsIGRhdGEgcGlwZSBzeW50YXgNCg0KDQojIGZvciByZWFkaW5nIGRhdGEgaW4gbXVsdGlwbGUgZm9ybWF0cw0KcmVxdWlyZShyZWFkeGwpDQpyZXF1aXJlKGhhdmVuKQ0KDQojIHZpc3VhbCBhbmFseXNpcw0KcmVxdWlyZShnZ3Bsb3QyKQ0KcmVxdWlyZShHR2FsbHkpICMgZXh0ZW5zaW9ucyB0byBnZ3Bsb3QNCnJlcXVpcmUoZ3QpICMgd2VsbCBmb3JtYXR0ZWQgdGFibGVzDQojIGNsaWVudC1zaWRlIGludGVyYWN0aXZlIHB1Ymxpc2hhYmxlIGdyYXBoaWNzDQpyZXF1aXJlKHBsb3RseSkNCnJlcXVpcmUobGVhZmxldCkNCnJlcXVpcmUoY3Jvc3N0YWxrKQ0KcmVxdWlyZShodG1sd2lkZ2V0cykNCiMgc2VydmVyLXNpZGUgaW50ZXJhY3RpdmUgZ3JhcGhpY3MNCnJlcXVpcmUoc2hpbnkpDQpyZXF1aXJlKHNoaW55anMpDQojIENhbm5lZCBJbnRlcmFjdGl2ZSBFREEgDQpyZXF1aXJlKEV4UGFuRGFSKQ0KDQoNCmBgYA0KIyMgRXhwbG9yaW5nIEtVIEJvb2sgUHJvY2Vzc2luZyBDaGFyZ2VzDQpgYGB7ciAgfQ0KIyByZWFkIEtVIGRhdGEgZnJhbWUNCktVYnBjLmRmIDwtIHJlYWRfY3N2KCJQdWJsaWMgRGF0YS9vcGVuYXBjLWRlL2RhdGEvYnBjLmNzdiIpDQojIHJlYWQgRE9BQiBtZXRhZGF0YQ0KDQpzb3VyY2UoJ1B1YmxpYyBEYXRhL0RPQUIvZG9hYmluZ2VzdC5SJykNCkRPQUJtZXRhLmRmIDwtIGRvYWJGZXRjaCgpDQpgYGANCmBgYHtyICB9DQoNCg0KaGVhZChLVWJwYy5kZikNCmhlYWQoc3VtbWFyeShLVWJwYy5kZikpDQoNCmdncGxvdChkYXRhID0gS1VicGMuZGYsIGFlcyhLVWJwYy5kZiRpbnN0aXR1dGlvbikpICsgZ2VvbV9iYXIoKSANCg0KZ2dwbG90KGRhdGEgPSBLVWJwYy5kZiwgYWVzKEtVYnBjLmRmJGV1cm8pKSArIGdlb21faGlzdG9ncmFtKCkNCg0KYGBgDQojIyBHZW5lcmFsIEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMNCmBgYHtyICB9DQoNCmdncGxvdChkYXRhID0gS1VicGMuZGYpICsgZ2VvbV9iYXIobWFwcGluZyA9IGFlcyh4ID0gS1VicGMuZGYkZG9hYikpDQoNCiMgRGF0ZSB0byBEb2FiDQpkYXRlX2RvYWIgPC0gS1VicGMuZGYgJT4lIGdncGxvdChkYXRhID0gS1VicGMuZGYsIG1hcHBpbmcgPSBhZXMoeCA9IEtVYnBjLmRmJHBlcmlvZCwgY29sb3VyID0gS1VicGMuZGYkZG9hYikpICsgZ2VvbV9mcmVxcG9seShiaW53aWR0aCA9IDAuMSkNCmdncGxvdGx5KGRhdGVfZG9hYikNCg0KIyBwdWJsaXNoZXJfZXVybyA8LSBLVWJwYy5kZiAlPiUgDQojIGdncGxvdChkYXRhID0gS1VicGMuZGYsIG1hcHBpbmcgPSBhZXMoeCA9IEtVYnBjLmRmJHB1Ymxpc2hlciwgY29sb3VyID0gS1VicGMuZGYkZXVybykpICsgZ2VvbV9mcmVxcG9seShiaW53aWR0aCA9IDAuMSkNCg0KIyBJbnN0aXR1dGlvbiB0byBFdXJvDQppbnN0aXR1dGlvbl9ldXJvIDwtIEtVYnBjLmRmICU+JSBnZ3Bsb3QoZGF0YSA9IEtVYnBjLmRmLCBtYXBwaW5nID0gYWVzKHggPSBLVWJwYy5kZiRldXJvKSkgKyBnZW9tX2ZyZXFwb2x5KG1hcHBpbmcgPSBhZXMoY29sb3VyID0gS1VicGMuZGYkaW5zdGl0dXRpb24pLCBiaW53aWR0aCA9IDUwMCkNCg0KZ2dwbG90bHkoaW5zdGl0dXRpb25fZXVybykNCg0KYGBgDQojIyBJZGVhOiBQdWJsaXNoZXJzIHZzLiBDaGFyZ2VzDQojIyBRdWVzdGlvbjogSG93IGRvIHRoZSB0b3AgMjUlIG9mIHB1Ymxpc2hlcnMgZGl2aWRlIHVwIGNoYXJnZXMgKGluIEV1cm8pPw0KIyMgT2JzZXJ2YXRpb246IENoYXJnZXMgYXJlIGdyb3VwZWQgYXJvdW5kIH4yMDAwIEV1cm9zIGFuZCB+ODAwMCBFdXJvcy4gDQpgYGB7ciAgfQ0KDQpwdWJsaXNoZXJfY291bnRzIDwtIEtVYnBjLmRmICU+JQ0KICAgIGdyb3VwX2J5KHB1Ymxpc2hlcikgJT4lDQogICAgdGFsbHkNCg0Kc29ydGVkX2NvdW50cyA9IGFycmFuZ2UocHVibGlzaGVyX2NvdW50cywgZGVzYyhuKSkNCg0KdG90YWxfbiA9IHN1bShzb3J0ZWRfY291bnRzJG4pDQpxdWFydGVyX24gPSAwLjI1ICogdG90YWxfbg0KbmV3X24gPSBzdW0oc29ydGVkX2NvdW50cyRuWzA6Nl0pDQoNCnNvcnRlZF9jb3VudHMgJT4lIGZpbHRlcihuID4gMjQpDQoNCiMgZmlsdGVyZWQgPC0gZmlsdGVyKEtVYnBjLmRmJHB1Ymxpc2hlciAlaW4lIHNvcnRlZF9jb3VudHMkcHVibGlzaGVyKQ0KDQpmaWx0ZXJlZCA8LSBmaWx0ZXIoS1VicGMuZGYsIEtVYnBjLmRmJHB1Ymxpc2hlciA9PSAndHJhbnNjcmlwdCBWZXJsYWcnIHwNCiAgICAgICAgICAgICAgICAgICAgIEtVYnBjLmRmJHB1Ymxpc2hlciA9PSAnRHVrZSBVbml2ZXJzaXR5IFByZXNzJyB8DQogICAgICAgICAgICAgICAgICAgICBLVWJwYy5kZiRwdWJsaXNoZXIgPT0gJ1VuaXZlcnNpdHkgb2YgTWljaGlnYW4gUHJlc3MnIHwNCiAgICAgICAgICAgICAgICAgICAgIEtVYnBjLmRmJHB1Ymxpc2hlciA9PSAnTWFuY2hlc3RlciBVbml2ZXJzaXR5IFByZXNzJyB8DQogICAgICAgICAgICAgICAgICAgICBLVWJwYy5kZiRwdWJsaXNoZXIgPT0gJ1BsdXRvIFByZXNzJyB8DQogICAgICAgICAgICAgICAgICAgICBLVWJwYy5kZiRwdWJsaXNoZXIgPT0gJ0xpdmVycG9vbCBVbml2ZXJzaXR5IFByZXNzJykNCg0KaGVhZChmaWx0ZXJlZCkNCg0KZXVyb19wdWJsaXNoZXIgPC0gZmlsdGVyZWQgJT4lIA0KICBnZ3Bsb3QoZGF0YSA9IGZpbHRlcmVkLCBtYXBwaW5nID0gYWVzKHggPSBmaWx0ZXJlZCRwdWJsaXNoZXIsIHkgPSBmaWx0ZXJlZCRldXJvKSwgDQogICAgICAgICBhZXMoeCA9IGZpbHRlcmVkJHB1Ymxpc2hlciwgeSA9IGZpbHRlcmVkJGV1cm8pKSArIA0KICAjIGdlb21fY291bnQoYWVzKGNvbG9yID0gLi5uLi4sIHNpemUgPSBhZnRlcl9zdGF0KHByb3ApLCBncm91cCA9IGV1cm8pKSArIA0KICBnZW9tX2NvdW50KGFlcyhjb2xvciA9IC4ubi4uLCBncm91cCA9IGV1cm8pKSArIA0KICBzY2FsZV9zaXplX2FyZWEobWF4X3NpemUgPSAxMCkgKyANCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMC43NSkpKSArDQogIGxhYnModGl0bGUgPSAiSG93IFB1Ymxpc2hlcnMgRGl2aWRlIENoYXJnZXMiLCB4ID0gIlRvcCAyNSUgb2YgUHVibGlzaGVycyIsIHkgPSAiUHJpY2UgKEV1cm8pIiwgY29sb3IgPSAnTnVtYmVyIG9mIENvcGllcycpICsNCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSBmdW5jdGlvbih4KSBzdHJfd3JhcChzdHJfcmVwbGFjZV9hbGwoeCwgImZvbyIsICIgIiksIHdpZHRoID0gMTcpKQ0KDQojIGdncGxvdDoNCmdncGxvdGx5KGV1cm9fcHVibGlzaGVyKQ0KDQojIGNyb3NzdGFsazoNCmZ0IDwtIGhpZ2hsaWdodF9rZXkoZmlsdGVyZWQpDQpnZ19mdCA8LSBnZ3Bsb3QoZGF0YSA9IGZ0LCBtYXBwaW5nID0gYWVzKHggPSBmaWx0ZXJlZCRwdWJsaXNoZXIsIHkgPSBmaWx0ZXJlZCRldXJvKSkgKyANCiAgZ2VvbV9jb3VudChhZXMoY29sb3IgPSAuLm4uLiwgc2l6ZSA9IGFmdGVyX3N0YXQocHJvcCksIGdyb3VwID0gZXVybykpICsgDQogIGxhYnModGl0bGUgPSAiSG93IFB1Ymxpc2hlcnMgRGl2aWRlIENoYXJnZXMiLCB4ID0gIlRvcCAyNSUgb2YgUHVibGlzaGVycyIsIHkgPSAiUHJpY2UgKEV1cm8pIiwgY29sb3IgPSAnTnVtYmVyIG9mIENvcGllcycpICsNCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSBmdW5jdGlvbih4KSBzdHJfd3JhcChzdHJfcmVwbGFjZV9hbGwoeCwgImZvbyIsICIgIiksIHdpZHRoID0gMTcpKQ0KY3Jvc3NfZnQgPC0gYnNjb2xzKA0KICBmaWx0ZXJfc2VsZWN0KCJwdWJsaXNoZXIiLCAiU2VsZWN0IGEgcHVibGlzaGVyIiwgZnQsIH5wdWJsaXNoZXIpLA0KICBnZ3Bsb3RseShnZ19mdCwgZHluYW1pY1RpY2tzID0gVFJVRSksDQogIHdpZHRocyA9IGMoMTIsIDEyKQ0KKQ0KDQpic2NvbHMoY3Jvc3NfZnQpDQoNCiMgc2hhcmVkX2V1cm9fcHVibGlzaGVyIDwtIFNoYXJlZERhdGEkbmV3KGZpbHRlcmVkKQ0KIyBsZWFmbGV0KHNoYXJlZF9ldXJvX3B1Ymxpc2hlcikgJT4lIGFkZE1hcmtlcnMoKQ0KIyBkYXRhLnRhYmxlOjpkYXRhLnRhYmxlKHNoYXJlZF9ldXJvX3B1Ymxpc2hlcikNCg0KDQpgYGANCiMjIElkZWE6IFB1Ymxpc2hlcnMnIENoYXJnZXMgdnMuIFllYXIvT0EgVHlwZQ0KIyMgU3ViLVF1ZXN0aW9uOiBXaGF0IGJlc3QgZXhwbGFpbnMgdGhlIHBhcnRpY3VsYXIgZGl2aXNpb24gb2YgY2hhcmdlcz8gKFllYXIsIE9BIFR5cGUpDQojIyBPYnNlcnZhdGlvbjogVGhlIGxvdyBhbmQgaGlnaCBjaGFyZ2UgZ3JvdXBzIHNlZW0gdG8gYmUgZGVmaW5lZCBieSB0aGUgdHlwZSBvZiBPQSBidXNpbmVzcyBtb2RlbCwgd2hlcmVhcyB0aGUgc2xpZ2h0IGRpZmZlcmVuY2VzIHdpdGhpbiBlYWNoIGdyb3VwIHNlZW0gdG8gYmUgZGVmaW5lZCBieSB0aGUgeWVhci4gDQpgYGB7ciAgfQ0KDQpoZWFkKGZpbHRlcmVkKQ0KDQojIERvZXMgVHlwZSBvZiBPQSBpbXBhY3QgdGhlIHBhcnRpY3VsYXIgZGl2aXNpb24gb2YgY2hhcmdlcz8NCg0KZXVyb19vYV9wdWJsaXNoZXIgPC0gZmlsdGVyZWQgJT4lIA0KICBnZ3Bsb3QoZGF0YSA9IGZpbHRlcmVkLCBtYXBwaW5nID0gYWVzKHggPSBmaWx0ZXJlZCRiYWNrbGlzdF9vYSwgeSA9IGZpbHRlcmVkJGV1cm8pLCANCiAgICAgICAgIGFlcyh4ID0gZmlsdGVyZWQkYmFja2xpc3Rfb2EsIHkgPSBmaWx0ZXJlZCRldXJvKSkgKyANCiAgZ2VvbV9jb3VudChhZXMoY29sb3IgPSAuLm4uLiwgZ3JvdXAgPSBldXJvKSkgKyANCiAgc2NhbGVfc2l6ZV9hcmVhKG1heF9zaXplID0gMTApICsgDQogIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDAuNzUpKSkgKw0KICBsYWJzKHRpdGxlID0gIkhvdyBPQSBJbXBhY3RzIFByaWNlIERpdmlzaW9uIG9mIENoYXJnZXMiLCB4ID0gIlR5cGUgb2YgT0EiLCB5ID0gIlByaWNlIChFdXJvKSIsIGNvbG9yID0gJ051bWJlciBvZiBDb3BpZXMnKQ0KDQojIGdncGxvdDoNCmdncGxvdGx5KGV1cm9fb2FfcHVibGlzaGVyKQ0KDQojIGNyb3NzdGFsazoNCmZ0IDwtIGhpZ2hsaWdodF9rZXkoZmlsdGVyZWQpDQpnZ19mdCA8LSBnZ3Bsb3QoZGF0YSA9IGZ0LCBtYXBwaW5nID0gYWVzKHggPSBmaWx0ZXJlZCRiYWNrbGlzdF9vYSwgeSA9IGZpbHRlcmVkJGV1cm8pKSArIA0KICBnZW9tX2NvdW50KGFlcyhjb2xvciA9IC4ubi4uLCBzaXplID0gYWZ0ZXJfc3RhdChwcm9wKSwgZ3JvdXAgPSBldXJvKSkgKyANCiAgbGFicyh0aXRsZSA9ICJIb3cgT0EgSW1wYWN0cyBEaXZpc2lvbiBvZiBDaGFyZ2VzIiwgeCA9ICJUeXBlIG9mIE9BIiwgeSA9ICJQcmljZSAoRXVybykiLCBjb2xvciA9ICdOdW1iZXIgb2YgQ29waWVzJykNCmNyb3NzX2Z0IDwtIGJzY29scygNCiAgZmlsdGVyX3NlbGVjdCgicHVibGlzaGVyIiwgIlNlbGVjdCBhIHB1Ymxpc2hlciIsIGZ0LCB+cHVibGlzaGVyKSwNCiAgZ2dwbG90bHkoZ2dfZnQsIGR5bmFtaWNUaWNrcyA9IFRSVUUpLA0KICB3aWR0aHMgPSBjKDEyLCAxMikNCikNCg0KYnNjb2xzKGNyb3NzX2Z0KQ0KDQoNCiMgRG9lcyBZZWFyIGltcGFjdCB0aGUgcGFydGljdWxhciBkaXZpc2lvbiBvZiBjaGFyZ2VzPw0KDQpldXJvX3llYXJfcHVibGlzaGVyIDwtIGZpbHRlcmVkICU+JSANCiAgZ2dwbG90KGRhdGEgPSBmaWx0ZXJlZCwgbWFwcGluZyA9IGFlcyh4ID0gZmlsdGVyZWQkcGVyaW9kLCB5ID0gZmlsdGVyZWQkZXVybyksIA0KICAgICAgICAgYWVzKHggPSBmaWx0ZXJlZCRwZXJpb2QsIHkgPSBmaWx0ZXJlZCRldXJvKSkgKyANCiAgZ2VvbV9jb3VudChhZXMoY29sb3IgPSAuLm4uLiwgZ3JvdXAgPSBldXJvKSkgKyANCiAgc2NhbGVfc2l6ZV9hcmVhKG1heF9zaXplID0gMTApICsgDQogIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDAuNzUpKSkgKw0KICBsYWJzKHRpdGxlID0gIkhvdyBZZWFyIEltcGFjdHMgUHJpY2UgRGl2aXNpb24gb2YgQ2hhcmdlcyIsIHggPSAiWWVhciIsIHkgPSAiUHJpY2UgKEV1cm8pIiwgY29sb3IgPSAnTnVtYmVyIG9mIENvcGllcycpDQoNCiMgZ2dwbG90Og0KZ2dwbG90bHkoZXVyb195ZWFyX3B1Ymxpc2hlcikNCg0KIyBjcm9zc3RhbGs6DQpmdCA8LSBoaWdobGlnaHRfa2V5KGZpbHRlcmVkKQ0KZ2dfZnQgPC0gZ2dwbG90KGRhdGEgPSBmdCwgbWFwcGluZyA9IGFlcyh4ID0gZmlsdGVyZWQkcGVyaW9kLCB5ID0gZmlsdGVyZWQkZXVybykpICsgDQogIGdlb21fY291bnQoYWVzKGNvbG9yID0gLi5uLi4sIHNpemUgPSBhZnRlcl9zdGF0KHByb3ApLCBncm91cCA9IGV1cm8pKSArIA0KICBsYWJzKHRpdGxlID0gIkhvdyBZZWFyIEltcGFjdHMgRGl2aXNpb24gb2YgQ2hhcmdlcyIsIHggPSAiWWVhciIsIHkgPSAiUHJpY2UgKEV1cm8pIiwgY29sb3IgPSAnTnVtYmVyIG9mIENvcGllcycpDQpjcm9zc19mdCA8LSBic2NvbHMoDQogIGZpbHRlcl9zZWxlY3QoInB1Ymxpc2hlciIsICJTZWxlY3QgYSBwdWJsaXNoZXIiLCBmdCwgfnB1Ymxpc2hlciksDQogIGdncGxvdGx5KGdnX2Z0LCBkeW5hbWljVGlja3MgPSBUUlVFKSwNCiAgd2lkdGhzID0gYygxMiwgMTIpDQopDQoNCmJzY29scyhjcm9zc19mdCkNCg0KDQpgYGANCiMjIElkZWE6IFB1Ymxpc2hlcnMgdnMuIE9BDQojIyBRdWVzdGlvbjogV2hhdCB0eXBlIG9mIGJ1c2luZXNzIG1vZGVsIGRvIHRoZSB0b3AgMjUlIHB1Ymxpc2hlcnMgdXNlPw0KIyMgT2JzZXJ2YXRpb246IE1vc3QgaGF2ZSBhIGhpZ2hlciBwcm9wb3J0aW9uIG9mIFRydWUgKG1vdmVkIHRvIE9BIGZyb20gdHJhZGl0aW9uYWwgcHVibGlzaGluZykgdGhhbiBGYWxzZSAoYWxyZWFkeSBwdWJsaXNoZWQgT0EpLg0KYGBge3IgIH0NCg0Kb2FfdHlwZSA8LSBmaWx0ZXJlZCAlPiUgDQogIGdncGxvdChkYXRhID0gZmlsdGVyZWQsIG1hcHBpbmcgPSBhZXMoeCA9IGZpbHRlcmVkJHB1Ymxpc2hlciwgY29sb3VyID0gZmlsdGVyZWQkYmFja2xpc3Rfb2EpLCBmaWxsID0gZmlsdGVyZWQkYmFja2xpc3Rfb2EpICsNCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIsIHdpZHRoID0gMC43LCBmaWxsPSIjRUFFQUVBIikgKw0KICBsYWJzKHRpdGxlID0gIkJ1c2luZXNzIE1vZGVsIE9BIGZvciBQdWJsaXNoZXJzIiwgeCA9ICJUb3AgMjUlIG9mIFB1Ymxpc2hlcnMiLCB5ID0gIlByb3BvcnRpb24iLCBjb2xvciA9ICdUeXBlcyBvZiBPQScpICsNCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMC43NSkpKSArDQogIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gZnVuY3Rpb24oeCkgc3RyX3dyYXAoc3RyX3JlcGxhY2VfYWxsKHgsICJmb28iLCAiICIpLCB3aWR0aCA9IDE3KSkgKw0KICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIikNCg0KZ2dwbG90bHkob2FfdHlwZSkNCg0KIyBjcm9zc3RhbGs6DQpmdCA8LSBoaWdobGlnaHRfa2V5KGZpbHRlcmVkKQ0Kb2FfZnQgPC0gZ2dwbG90KGRhdGEgPSBmdCwgbWFwcGluZyA9IGFlcyh4ID0gZnQkcHVibGlzaGVyLCBjb2xvdXIgPSBmdCRiYWNrbGlzdF9vYSksIGZpbGwgPSBmdCRiYWNrbGlzdF9vYSkgKw0KICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJmaWxsIiwgd2lkdGggPSAwLjcpICsNCiAgbGFicyh0aXRsZSA9ICJCdXNpbmVzcyBNb2RlbCBPQSBmb3IgUHVibGlzaGVycyIsIHggPSAiVG9wIDI1JSBvZiBQdWJsaXNoZXJzIiwgeSA9ICJQcm9wb3J0aW9uIG9mIEJhY2tsaXN0IE9BIiwgY29sb3IgPSAnVHlwZXMgb2YgT0EnKSArDQogIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDAuNzUpKSkgKw0KICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGZ1bmN0aW9uKHgpIHN0cl93cmFwKHN0cl9yZXBsYWNlX2FsbCh4LCAiZm9vIiwgIiAiKSwgd2lkdGggPSAxNykpDQojIGNyb3NzX29hX2Z0IDwtIGJzY29scygNCiMgICBmaWx0ZXJfc2VsZWN0KCJwdWJsaXNoZXIiLCAiU2VsZWN0IGEgcHVibGlzaGVyIiwgZnQsIH5wdWJsaXNoZXIpLA0KIyAgIGdncGxvdGx5KG9hX2Z0LCBkeW5hbWljVGlja3MgPSBUUlVFKSwNCiMgICAjIHdpZHRocyA9IGMoMTIsIDEyKQ0KIyApDQoNCiMgYnNjb2xzKGNyb3NzX29hX2Z0KQ0KDQoNCmBgYA0KIyMgSWRlYTogUHVibGlzaGVycycgT0EgdnMuIFllYXINCiMjIFF1ZXN0aW9uOiBEaWQgT0EgYnVzaW5lc3MgbW9kZWxzIG9mIHRoZSB0b3AgMjUlIHB1Ymxpc2hlcnMgY2hhbmdlIHBlciB5ZWFyPw0KIyMgT2JzZXJ2YXRpb246DQpgYGB7ciAgfQ0KDQpvYV90aW1lIDwtIGZ1bmN0aW9uKHB1Yl9uYW1lKSB7DQogIHB1Yl9mdCA8LSBmaWx0ZXIoZmlsdGVyZWQsIGZpbHRlcmVkJHB1Ymxpc2hlciA9PSBwdWJfbmFtZSkNCiAgDQogIHB1Yl9vYSA8LSBwdWJfZnQgJT4lIA0KICAgIGdncGxvdChkYXRhID0gcHViX2Z0LCBtYXBwaW5nID0gYWVzKHggPSBwdWJfZnQkcGVyaW9kLCBjb2xvdXIgPSBwdWJfZnQkYmFja2xpc3Rfb2EpLCBmaWxsID0gcHViX2Z0JGJhY2tsaXN0X29hKSArDQogICAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIsIHdpZHRoID0gMC43LCBmaWxsPSIjRUFFQUVBIikgKw0KICAgIGxhYnModGl0bGUgPSBwYXN0ZShwdWJfbmFtZSwgIidzIE9BIFRocm91Z2ggdGhlIFllYXJzIiwgc2VwID0gIiIpLCANCiAgICAgICAgIHggPSAiWWVhcnMiLCB5ID0gIlByb3BvcnRpb24gb2YgQmFja2xpc3QgT0EiLCBjb2xvciA9ICdUeXBlcyBvZiBPQScpICsNCiAgICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCgwLjc1KSkpICsNCiAgICBzY2FsZV94X2Rpc2NyZXRlKGxpbWl0cz1jKDIwMTcsIDIwMTgsIDIwMTkpKSArDQogICAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGUgPSAiU2V0MSIpDQoNCiAgZ2dwbG90bHkocHViX29hKQ0KICANCn0NCg0KdG9wMjVfbGlzdCA9IGMoInRyYW5zY3JpcHQgVmVybGFnIiwgIkR1a2UgVW5pdmVyc2l0eSBQcmVzcyIsICJVbml2ZXJzaXR5IG9mIE1pY2hpZ2FuIFByZXNzIiwgIk1hbmNoZXN0ZXIgVW5pdmVyc2l0eSBQcmVzcyIsICJQbHV0byBQcmVzcyIsICJMaXZlcnBvb2wgVW5pdmVyc2l0eSBQcmVzcyIpDQoNCm9hX3RpbWUoInRyYW5zY3JpcHQgVmVybGFnIikNCg0Kb2FfdGltZSgiRHVrZSBVbml2ZXJzaXR5IFByZXNzIikNCg0Kb2FfdGltZSgiVW5pdmVyc2l0eSBvZiBNaWNoaWdhbiBQcmVzcyIpDQoNCm9hX3RpbWUoIk1hbmNoZXN0ZXIgVW5pdmVyc2l0eSBQcmVzcyIpDQoNCm9hX3RpbWUoIlBsdXRvIFByZXNzIikNCg0Kb2FfdGltZSgiTGl2ZXJwb29sIFVuaXZlcnNpdHkgUHJlc3MiKQ0KDQpgYGANCiMjIElkZWE6IFJldmVudWUgdnMuIE9BDQojIyBRdWVzdGlvbjogV2hhdCB0b3RhbCByZXZlbnVlIGFyZSBwdWJsaXNoZXJzIHJlY2VpdmluZyBlYWNoIHllYXI/DQojIyBPYnNlcnZhdGlvbjogDQpgYGB7ciAgfQ0KDQojIEZpbmRpbmcgdG90YWwgcmV2ZW51ZSBmb3IgZWFjaCBwdWJsaXNoZXINCg0KcmV2ZW51ZV9maW5kZXIgPC0gZnVuY3Rpb24ocHViX25hbWUpIHsNCiAgcHViX2ZpbHRlcmVkIDwtIGZpbHRlcihmaWx0ZXJlZCwgZmlsdGVyZWQkcHVibGlzaGVyID09IHB1Yl9uYW1lKQ0KICByZXYgPSBzdW0ocHViX2ZpbHRlcmVkJGV1cm8pDQp9DQoNCnJldmVudWVfZGYgPC0gZGF0YS5mcmFtZSgicHVibGlzaGVyIiA9IHRvcDI1X2xpc3QpDQpyZXZlbnVlX2xpc3QgPC0gYygpDQoNCmZvciAoaSBpbiB0b3AyNV9saXN0KSB7DQogIHJldmVudWVfbGlzdDwtYyhyZXZlbnVlX2xpc3QscmV2ZW51ZV9maW5kZXIoaSkpDQp9DQoNCnJldmVudWVfZGYkcmV2ZW51ZSA8LSBjKHJldmVudWVfbGlzdCkNCnByaW50KHJldmVudWVfZGYpDQoNCiMgZ2dwbG90Og0KcHVibGlzaGVyX3JldmVudWUgPC0gcmV2ZW51ZV9kZiAlPiUNCiAgZ2dwbG90KGRhdGEgPSByZXZlbnVlX2RmLCBtYXBwaW5nID0gYWVzKHggPSByZXZlbnVlX2RmJHB1Ymxpc2hlciwgeSA9IHJldmVudWVfZGYkcmV2ZW51ZSksIGZpbGwgPSByZXZlbnVlX2RmJHJldmVudWUpICsNCiAgZ2VvbV9jb2woKSArDQogIGxhYnModGl0bGUgPSAiVG90YWwgUmV2ZW51ZSBmb3IgUHVibGlzaGVycyIsIHggPSAiVG9wIDI1JSBvZiBQdWJsaXNoZXJzIiwgeSA9ICJSZXZlbnVlIChFdXJvKSIsIGNvbG9yID0gJ1R5cGVzIG9mIE9BJykgKw0KICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCgwLjc1KSkpICsNCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSBmdW5jdGlvbih4KSBzdHJfd3JhcChzdHJfcmVwbGFjZV9hbGwoeCwgImZvbyIsICIgIiksIHdpZHRoID0gMTcpKSArDQogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MSIpDQoNCmdncGxvdGx5KHB1Ymxpc2hlcl9yZXZlbnVlKQ0KDQoNCmBgYA0KIyMgSWRlYTogUmV2ZW51ZSB2cy4gT0ENCiMjIFF1ZXN0aW9uOiBXaGF0IHJldmVudWUgYXJlIHB1Ymxpc2hlcnMgcmVjZWl2aW5nIHBlciB5ZWFyPw0KIyMgT2JzZXJ2YXRpb246IA0KYGBge3IgIH0NCg0KIyBGaW5kaW5nIHRvdGFsIHJldmVudWUgZm9yIGVhY2ggcHVibGlzaGVyDQoNCnJldmxpc3RfMjAxNyA8LSBjKCkNCnJldmxpc3RfMjAxOCA8LSBjKCkNCnJldmxpc3RfMjAxOSA8LSBjKCkNCg0KcmV2bGlzdCA8LSBjKCkNCg0KZm9yIChuYW1lIGluIHRvcDI1X2xpc3QpIHsNCiAgcHViX25hbWUgPC0gZmlsdGVyKGZpbHRlcmVkLCBmaWx0ZXJlZCRwdWJsaXNoZXIgPT0gbmFtZSkNCiAgcmV2XzIwMTcgPSBzdW0ocHViX25hbWVbcHViX25hbWUkcGVyaW9kID09IDIwMTcsXSRldXJvKQ0KICByZXZsaXN0XzIwMTcgPC0gYyhyZXZsaXN0XzIwMTcsIHJldl8yMDE3KQ0KICByZXZfMjAxOCA9IHN1bShwdWJfbmFtZVtwdWJfbmFtZSRwZXJpb2QgPT0gMjAxOCxdJGV1cm8pDQogIHJldmxpc3RfMjAxOCA8LSBjKHJldmxpc3RfMjAxOCwgcmV2XzIwMTgpDQogIHJldl8yMDE5ID0gc3VtKHB1Yl9uYW1lW3B1Yl9uYW1lJHBlcmlvZCA9PSAyMDE5LF0kZXVybykNCiAgcmV2bGlzdF8yMDE5IDwtIGMocmV2bGlzdF8yMDE5LCByZXZfMjAxOSkNCn0NCg0KcmV2ZW51ZV9kZiA8LSBkYXRhLmZyYW1lKCJwdWJsaXNoZXIiID0gdG9wMjVfbGlzdCkNCnJldmVudWVfZGYkJzIwMTcnIDwtIGMocmV2bGlzdF8yMDE3KQ0KcmV2ZW51ZV9kZiQnMjAxOCcgPC0gYyhyZXZsaXN0XzIwMTgpDQpyZXZlbnVlX2RmJCcyMDE5JyA8LSBjKHJldmxpc3RfMjAxOSkNCg0KcHJpbnQocmV2ZW51ZV9kZikNCg0KcmV2ZW51ZV95ZWFyIDwtIGMocmV2ZW51ZV9kZiQnMjAxNycsIHJldmVudWVfZGYkJzIwMTgnLCByZXZlbnVlX2RmJCcyMDE5JykNCnllYXIgPC0gYygnMjAxNycsICcyMDE4JywgJzIwMTknKQ0KDQojIGdncGxvdDoNCnB1Yl95ZWFyX3JldmVudWUxIDwtIHJldmVudWVfZGYgJT4lDQogIA0KICBnZ3Bsb3QoZGF0YSA9IHJldmVudWVfZGYsIG1hcHBpbmcgPSBhZXMoeCA9ICcyMDE3JywgeSA9IHJldmVudWVfZGYkJzIwMTcnLCBmaWxsID0gcmV2ZW51ZV9kZiRwdWJsaXNoZXIpKSArDQogIGdlb21fYmFyKHBvc2l0aW9uPSJkb2RnZSIsIHN0YXQ9ImlkZW50aXR5IikgKw0KICBsYWJzKHRpdGxlID0gIlRvdGFsIFJldmVudWUgZm9yIFB1Ymxpc2hlcnMiLCB4ID0gIlllYXIiLCB5ID0gIlJldmVudWUgKEV1cm8pIiwgY29sb3IgPSAnUHVibGlzaGVycycpICsNCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMC43NSkpKQ0KDQpnZ3Bsb3RseShwdWJfeWVhcl9yZXZlbnVlMSkNCg0KcHViX3llYXJfcmV2ZW51ZTIgPC0gcmV2ZW51ZV9kZiAlPiUNCiAgDQogIGdncGxvdChkYXRhID0gcmV2ZW51ZV9kZiwgbWFwcGluZyA9IGFlcyh4ID0gJzIwMTgnLCB5ID0gcmV2ZW51ZV9kZiQnMjAxOCcsIGZpbGwgPSByZXZlbnVlX2RmJHB1Ymxpc2hlcikpICsNCiAgZ2VvbV9iYXIocG9zaXRpb249ImRvZGdlIiwgc3RhdD0iaWRlbnRpdHkiKSArDQogIGxhYnModGl0bGUgPSAiVG90YWwgUmV2ZW51ZSBmb3IgUHVibGlzaGVycyIsIHggPSAiWWVhciIsIHkgPSAiUmV2ZW51ZSAoRXVybykiLCBjb2xvciA9ICdQdWJsaXNoZXJzJykgKw0KICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCgwLjc1KSkpDQoNCmdncGxvdGx5KHB1Yl95ZWFyX3JldmVudWUyKQ0KDQpwdWJfeWVhcl9yZXZlbnVlMyA8LSByZXZlbnVlX2RmICU+JQ0KICANCiAgZ2dwbG90KGRhdGEgPSByZXZlbnVlX2RmLCBtYXBwaW5nID0gYWVzKHggPSAnMjAxOScsIHkgPSByZXZlbnVlX2RmJCcyMDE5JywgZmlsbCA9IHJldmVudWVfZGYkcHVibGlzaGVyKSkgKw0KICBnZW9tX2Jhcihwb3NpdGlvbj0iZG9kZ2UiLCBzdGF0PSJpZGVudGl0eSIpICsNCiAgbGFicyh0aXRsZSA9ICJUb3RhbCBSZXZlbnVlIGZvciBQdWJsaXNoZXJzIiwgeCA9ICJZZWFyIiwgeSA9ICJSZXZlbnVlIChFdXJvKSIsIGNvbG9yID0gJ1B1Ymxpc2hlcnMnKSArDQogIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDAuNzUpKSkNCg0KZ2dwbG90bHkocHViX3llYXJfcmV2ZW51ZTMpDQoNCnJldmVudWVfZGYgJT4lIGdhdGhlcignMjAxNycsJzIwMTgnLCcyMDE5Jywga2V5PSJ5ZWFyIiwgdmFsdWU9IkV1cm8iKSAlPiUgZ2dwbG90KGFlcyh4PXB1Ymxpc2hlcix5PUV1cm8pLGZpbGw9cHVibGlzaGVyKSArIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikgKyBmYWNldF93cmFwKCJ5ZWFyIikNCg0KYGBgDQojIyMgQ29udGludWVkLCB0cmllZCBwdXR0aW5nIGl0IGludG8gb25lIGdyYXBoLiANCmBgYHtyfQ0KDQpyZXZsaXN0IDwtIGMoKQ0KcmV2bGlzdF8yMDE3IDwtIGMoKQ0KcmV2bGlzdF8yMDE4IDwtIGMoKQ0KcmV2bGlzdF8yMDE5IDwtIGMoKQ0KDQpmb3IgKG5hbWUgaW4gdG9wMjVfbGlzdCkgew0KICBwdWJfbmFtZSA8LSBmaWx0ZXIoZmlsdGVyZWQsIGZpbHRlcmVkJHB1Ymxpc2hlciA9PSBuYW1lKQ0KICByZXZfMjAxNyA9IHN1bShwdWJfbmFtZVtwdWJfbmFtZSRwZXJpb2QgPT0gMjAxNyxdJGV1cm8pDQogIHJldmxpc3RfMjAxNyA8LSBjKHJldmxpc3RfMjAxNywgcmV2XzIwMTcpDQogIHJldl8yMDE4ID0gc3VtKHB1Yl9uYW1lW3B1Yl9uYW1lJHBlcmlvZCA9PSAyMDE4LF0kZXVybykNCiAgcmV2bGlzdF8yMDE4IDwtIGMocmV2bGlzdF8yMDE4LCByZXZfMjAxOCkNCiAgcmV2XzIwMTkgPSBzdW0ocHViX25hbWVbcHViX25hbWUkcGVyaW9kID09IDIwMTksXSRldXJvKQ0KICByZXZsaXN0XzIwMTkgPC0gYyhyZXZsaXN0XzIwMTksIHJldl8yMDE5KQ0KfQ0KDQpyZXZsaXN0IDwtIGMocmV2bGlzdF8yMDE3LCByZXZsaXN0XzIwMTgsIHJldmxpc3RfMjAxOSkNCg0KcHJpbnQocmV2bGlzdCkNCg0KbnJldiA8LSBtYXRyaXgocmV2bGlzdCwgbmNvbD02LCBieXJvdz1UUlVFKQ0KY29sbmFtZXMobnJldikgPC0gdG9wMjVfbGlzdA0Kcm93bmFtZXMobnJldikgPC0gYygiMjAxNyIsICIyMDE4IiwgIjIwMTkiKQ0KbnJldiA8LSBhcy50YWJsZShucmV2KQ0KbnJldiA8LSBhcy5kYXRhLmZyYW1lLm1hdHJpeChucmV2KQ0KDQpwcmludChucmV2KQ0KDQojLCBucmV2JGBEdWtlIFVuaXZlcnNpdHkgUHJlc3NgLCBucmV2JGBVbml2ZXJzaXR5IG9mIE1pY2hpZ2FuIFByZXNzYCwgbnJldiRgUGx1dG8gUHJlc3NgLCBucmV2JGBNYW5jaGVzdGVyIFVuaXZlcnNpdHkgUHJlc3NgLCBucmV2JGBMaXZlcnBvb2wgVW5pdmVyc2l0eSBQcmVzc2ANCg0KcHViX3llYXJfcmV2IDwtIG5yZXYgJT4lDQogIA0KICBnZ3Bsb3QoZGF0YSA9IG5yZXYsIG1hcHBpbmcgPSBhZXMoeCA9IGMoIjIwMTciLCAiMjAxOCIsICIyMDE5IiksIHkgPSBjKG5yZXYkInRyYW5zY3JpcHQgVmVybGFnIiksIGZpbGwgPSBucmV2JHB1Ymxpc2hlcikpICsNCiAgZ2VvbV9iYXIocG9zaXRpb249ImRvZGdlIiwgc3RhdD0iaWRlbnRpdHkiKSArDQogIGxhYnModGl0bGUgPSAiVG90YWwgUmV2ZW51ZSBmb3IgUHVibGlzaGVycyIsIHggPSAiWWVhciIsIHkgPSAiUmV2ZW51ZSAoRXVybykiLCBjb2xvciA9ICdQdWJsaXNoZXJzJykgKw0KICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCgwLjc1KSkpDQoNCmdncGxvdGx5KHB1Yl95ZWFyX3JldikNCg0KYGBgDQojIyBJZGVhOiBET0FCIGFuYWx5c2lzDQojIyBRdWVzdGlvbjogV2hhdCBpcyB0aGUgYXZlcmFnZSB0aW1lIGdhcCBiZXR3ZWVuIHllYXIgb2YgcHVibGljYXRpb24gYW5kIGFkZGVkIG9uIGRhdGU/IA0KIyMgT2JzZXJ2YXRpb246IA0KYGBge3J9DQoNCkRPQUJtZXRhLmRmIDwtIGZpbHRlcihET0FCbWV0YS5kZiwgaXMubmEoRE9BQm1ldGEuZGYkWWVhci5vZi5wdWJsaWNhdGlvbikpDQpwcmludChET0FCbWV0YS5kZiRZZWFyLm9mLnB1YmxpY2F0aW9uWzE6NF0pDQpnYXAgPSBtZWFuKERPQUJtZXRhLmRmJEFkZGVkLm9uLmRhdGUgLSBET0FCbWV0YS5kZiRZZWFyLm9mLnB1YmxpY2F0aW9uWzE6M10pDQpwcmludChnYXApDQoNCmBgYA0KIyMjIENvbXBhcmlzb24gb2YgY2hhcmdlcyBieSB5ZWFyIGFuZCBiYWNrbGlzdA0KYGBge3J9DQojIGNyZWF0ZSBmYWNldGVkIHBsb3Qgb2JqZWN0DQpjaGFyZ2VzLnBsb3QgPC0gS1VicGMuZGYgJT4lIGdncGxvdChhZXMoZXVybykpK2dlb21faGlzdG9ncmFtKGJpbnM9NikrZmFjZXRfZ3JpZChyb3dzPXZhcnMocGVyaW9kKSwgY29scyA9IHZhcnMoYmFja2xpc3Rfb2EpKQ0KDQoNCiMjIFByZXNlbnQgYXMgU3RhbmRhcmQgcGxvdA0KIHBsb3QoY2hhcmdlcy5wbG90KQ0KDQojIHRoaXMgcGxvdCB3aWxsIHJlbmRlciBwdWJsaWNseSBodHRwczovL2h0bWxwcmV2aWV3LmdpdGh1Yi5pby8/aHR0cHM6Ly9naXRodWIuY29tL01JVC1JbmZvcm1hdGljcy9tb25vZ3JhcGgvYmxvYi9tYXN0ZXIvMDAlMjBFREElMjBTdGFydC5uYi5odG1sDQoNCmBgYA0KIyMjIEludGVyYWN0aXZlIGNoYXJnZXMgZXhwbG9yYXRpb24NCmBgYHtyfQ0KIGdncGxvdGx5KGNoYXJnZXMucGxvdCkNCiMgaHR0cHM6Ly9taXQtaW5mb3JtYXRpY3MuZ2l0aHViLmlvL21vbm9ncmFwaC9kZW1vLmh0bWwNCg0KYGBgDQpgYGANCiMjIyBJbnRlcmFjdGl2ZSBEYXRhc2V0IEV4cGxvcmF0aW9uIA0KYGBgDQpgYGB7cn0NCktVYnBjLmRmICU+JSBFeFBhbkQoZGY9LiAgICAgICAsdGl0bGU9IktVIEJvb2sgUHJvY2Vzc2luZyBDaGFyZ2VzIixleHBvcnRfbmJfb3B0aW9uID0gVFJVRSkNCiMgRXhQYW5EIHVzZXMgc2hpbnkoKSB3aGljaCB3b3JrcyBydW5uaW5nIFIgbG9jYWxseSwgYnV0IGlzbid0IGdvaW5nIHRvIHdvcmsgdGhyb3VnaCBnaXRodWIuIENvdWxkIHB1Ymxpc2ggdGhyb3VnaCBzaGlueWFwcHMuaW8gKGxvdyB1c2FnZSBvbmx5KSwgb3IgZXhwb3J0ICBhIG5vbi1pbnRlcmFjdGl2ZSBub3RlYm9vayBpdA0KIyBzZWU6IGh0dHBzOi8vZHJtYWx0bWFuLnNoaW55YXBwcy5pby9kZW1vLw0KYGBgDQoNCg==